/**************************************************************************************************
* Title: SumMeshManager.cpp
* Author: Gael Huber
* Description: Specific resource group for handling mesh objects. 
**************************************************************************************************/
#include "SumMeshManager.h"

/**************************************************************************************************
* Initialize singleton instance to 0
**************************************************************************************************/
template <> MeshManager* Singleton<MeshManager>::singleton = 0;

/**************************************************************************************************
* Default constructor
**************************************************************************************************/
MeshManager::MeshManager(void)
{ }

/**************************************************************************************************
* Destructor
**************************************************************************************************/
MeshManager::~MeshManager(void)
{ }

/**************************************************************************************************
* Initialize resource group. Resource groups are initialized by reading in filenames in given 
* directories and associating names with resources. The actual resources are not loaded until they
* are needed.
**************************************************************************************************/
void MeshManager::initializeResourceGroup(const String& name, const List<String>& directories) {
	// Iterate through the list
	List<String>::Iterator e = directories.end();
	for(List<String>::Iterator itr = directories.begin(); itr != e; ++itr) {
		intptr_t handle, resource;
		_finddata_t ffd;

		// Find the first file
		handle = _findfirst((*itr + "\\*.obj").c_str(), &ffd);	
		resource = 0;

		// While we have a valid read
		while(handle != -1 && resource != -1) {
			// Valid file names
			String name = ffd.name;
			// Create a new mesh association
			_resourcePaths[name] = *itr + "\\" + name;
			_resources[name] = 0;
			
			// Get the next file
			resource = _findnext(handle, &ffd);
		}
		_findclose(handle);
	}
}

/**************************************************************************************************
* Unload all resources present in the resource group
**************************************************************************************************/
void MeshManager::unloadResourceGroup(void) {
}

/**************************************************************************************************
* Unload a specific resource in the group
**************************************************************************************************/
void MeshManager::unloadResource(const String& name) {
}

/**************************************************************************************************
* Retrieve a mesh from the manager
**************************************************************************************************/
Mesh* MeshManager::retrieveMesh(const String& name) {
	return _retrieveOrCreateMesh(name);
}

/**************************************************************************************************
* Internal function used to retrieve/create a mesh object
**************************************************************************************************/
Mesh* MeshManager::_retrieveOrCreateMesh(const String& name) {
	// Test whether we need to load the mesh into memory
	if(_resources[name] == 0) {
		// The mesh isn't loaded yet, so perform load
		_resources[name] = _loadMesh(name);
	}

	// Return the mesh
	return _resources[name];
}

/**************************************************************************************************
* Load a mesh from file
**************************************************************************************************/
Mesh* MeshManager::_loadMesh(const String& name) {
	// Get filepath
	if(_resourcePaths[name] == "") {
		return 0;
	}

	// Perform load
	fstream file (_resourcePaths[name].c_str());
	Mesh* m = 0;
	
	if(file.is_open()) {
		// Initialize a mesh object
		m = new Mesh();

		// Array of vertices
		Array<Vertex> verts;

		// Array of indices
		Array<unsigned int> indices;

		char cLine[255];
		String line;
		while(!file.eof()) {
			// Get the line
			ReadLine(file, cLine, line);

			// Interpret the line
			if(line.at(0) == '#') {
				// Comment
			} else if(line.at(0) == 'o') {
				// Object name
				m->name = line.substring(line.begin() + 2, line.end());
			} else if(line.at(0) == 'v') {
				// Vertex
				Array<String> arr = line.split(' ');
				Vector3 v;
				v.x = static_cast<float>(atof(arr[1].c_str()));
				v.y = static_cast<float>(atof(arr[2].c_str()));
				v.z = static_cast<float>(atof(arr[3].c_str()));
				verts.push_back(Vertex(
					v,
					Vector4((v.x > 0) ? 1.0f : 0.0f, (v.y > 0) ? 1.0f : 0.0f, (v.z > 0) ? 1.0f : 0.0f, 1.0f)	// Arbitrary
					));
			} else if(line.at(0) == 'f') {
				// Face
				Array<String> arr = line.split(' ');
				if(arr.getCount() == 4) {
					// Triangle
					indices.push_back(atoi(arr[1].c_str()));
					indices.push_back(atoi(arr[2].c_str()));
					indices.push_back(atoi(arr[3].c_str()));
				} else if(arr.getCount() == 5) {
					// Square
					indices.push_back(atoi(arr[1].c_str()));
					indices.push_back(atoi(arr[2].c_str()));
					indices.push_back(atoi(arr[3].c_str()));
					indices.push_back(atoi(arr[4].c_str()));
					indices.push_back(atoi(arr[1].c_str()));
				}
			}
		}
		file.close();

		// Perform bindings to mesh data
		m->numVertices = verts.getCount();
		m->numIndices = indices.getCount();
		
		// Create the buffer description
		D3D11_BUFFER_DESC bd;
		ZeroMemory(&bd, sizeof(D3D11_BUFFER_DESC));
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.ByteWidth = sizeof(Vertex) * m->numVertices;
		bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		bd.CPUAccessFlags = 0;
	
		// Create the subresource data
		D3D11_SUBRESOURCE_DATA initData;
		ZeroMemory(&initData, sizeof(D3D11_SUBRESOURCE_DATA));
		initData.pSysMem = verts.getPtrToCopy();

		// Initialize the vertex buffer
		RenderManager::getSingletonPtr()->createBuffer(&bd, &initData, &m->vertexBuffer);

		// Create the index buffer
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.ByteWidth = sizeof(unsigned short) * m->numIndices;
		bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		initData.pSysMem = indices.getPtrToCopy();

		// Initialize index buffer
		RenderManager::getSingletonPtr()->createBuffer(&bd, &initData, &m->indexBuffer);
	}

	// Return created mesh
	return m;
}